package parser_protobuf3

import (
	"fmt"
	"path/filepath"
	"strings"

	"gitee.com/woodpile/wgs.ths.code.generate/definition"
	config "gitee.com/woodpile/wgs.ths.config.go"
	"github.com/jhump/protoreflect/desc/protoparse" //cspell: disable-line
)

func init() {
	definition.RegParser(&Parser{})

	config.DefaultContext.RegisterConfig("parser.plugins[protobuf3]", &ParserConfig{})
}

const (
	ParserName = "protobuf3"
)

var (
	protoParser = &protoparse.Parser{
		IncludeSourceCodeInfo: true,
	}
)

type ParserConfig struct {
	Ext []string `yaml:"ext,omitempty"`

	ImportPath []string `yaml:"importPath,omitempty"`
}

func (*ParserConfig) Name() string {
	return ParserName
}

func (*ParserConfig) ToDefault(cfg config.IConfigDefault) {
	ac := cfg.(*ParserConfig)
	if len(ac.Ext) == 0 {
		ac.Ext = []string{".proto"}
	}
}

type Parser struct {
	Config *ParserConfig

	ProtoOptions *ProtoOptions
}

func (p *Parser) Name() string {
	return ParserName
}

func (p *Parser) Make() (definition.Parser, error) {
	return NewParser()
}

func NewParser() (*Parser, error) {
	p := &Parser{}

	//配置检查
	cfgI, ok := definition.GlobalConfig.Parser.Plugins[ParserName]
	if !ok {
		return nil, fmt.Errorf("not found config for parser: %s", ParserName)
	}
	cfg, ok := cfgI.(*ParserConfig)
	if !ok {
		return nil, fmt.Errorf("invalid config for parser: %s", ParserName)
	}
	if len(cfg.ImportPath) == 0 {
		return nil, fmt.Errorf("invalid config for parser: %s, no import path", ParserName)
	}
	p.Config = cfg

	if definition.GlobalConfig.Verbose {
		fmt.Printf("parser config: %+v\n", cfg)
	}

	p.ProtoOptions = newProtoOptions()

	protoParser.ImportPaths = append([]string{"."}, p.Config.ImportPath...)
	return p, nil
}

func (p *Parser) Parse(ctx *definition.Context) error {
	if definition.GlobalConfig.Verbose {
		fmt.Println("protobuf3 parse start...")
		defer fmt.Println("protobuf3 parse end...")
	}

	if err := p.parseOptions(ctx); err != nil {
		return fmt.Errorf("parse options failed: %w", err)
	}

	if err := p.parseFiles(ctx); err != nil {
		return fmt.Errorf("parse files failed: %w", err)
	}

	if err := p.parseEnums(ctx); err != nil {
		return fmt.Errorf("parse enums failed: %w", err)
	}

	if err := p.parseMessages(ctx); err != nil {
		return fmt.Errorf("parse messages failed: %w", err)
	}

	return nil
}

func (p *Parser) filterFile(path string) bool {
	ext := filepath.Ext(path)
	for _, e := range p.Config.Ext {
		if ext == e {
			return true
		}
	}
	return false
}

func (p *Parser) getOrNewDFFile(ctx *definition.Context, dir, filename string) *definition.DFFile {
	fn := strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename))
	fp := filepath.Join(dir, filename)
	df := ctx.DFSet.GetFileByPath(fp)
	if df == nil {
		df = &definition.DFFile{}
		df.Name = fn
		df.Path = filepath.Join(dir, filename)
	}
	return df
}
